#!/usr/bin/python
###### ToDo ######
# [x] implement board
# [x] implement reverse space lookups on board
# [x] store reverse space lookups in Player()
# [x] added space to Player(data)
# [x] implement player class
# [x] implement player locations
# [x] implement player movement (up, down, side, side)
# [x] implement player diagonal movement
# [x] implement starting squares
# [x] implement player position data struct
# [x] implement player position awareness
# [x] implement player move validation
# [x] implement card class
# [x] implement draw random card
# [x] take a look at "None" return on dealcard() // and possibly a way to prevent it
# [x] implement message class
# [x] set up interactive part of game
# [x] make it so that each player initializes on a proper starting square
# [x] make it so that turn order is properly generated (could always just have miss scarlet first or go back and overwrite miss scarlet in the turn order?)
# [x] make a suggestion
# [x] suggestion response
# [x] make an accusation
# [x] prove an accusation false
# [x] implement turns
# [x] implement argparse (argument parsing and some player interactions)
###

# Board Layout
#-1 0 1 2 3 4 5
#         o     -1
#   0|a|1|b|2    0
# m c| |d| |e p  1
#   3|f|4|g|5    2
# n h| |i| |j    3
#   6|k|7|l|8    4
#     q   r      5

############################################################################################
# IMPORTS
############################################################################################

import argparse
import random
from random import choice
from time import sleep

#####################################################


############################################################################################
# VARIABLES & data structures
############################################################################################

# list of weapons (for cards)
weapon_names = ['wrench', 'candlestick', 'lead pipe', 'rope', 'revolver', 'knife']
# list of player names
player_names = ['miss scarlet', 'mrs. white', 'mrs. peacock', 'mr. green', 'colonel mustard', 'professor plum']
# list of rooms
rm_names = ['conservatory', 'lounge', 'kitchen', 'library', 'hall', 'study', 'ballroom', 'dining room', 'billiard room']
#all cards live in the ddeck dictionary
ddeck = {}
# subset - weapons cards
wdeck = {}
# subset - player cards
pdeck = {}
# subset room cards
rdeck = {}

# answer  --> data structure to hold the answer
answer = []

# opos == 'occupied positions'
# this position should be fed from the 'move' function of the player class
opos = {}

# players --> list of player objects
players = {}

cids = []
cards = {}

# active player
active_player = 0

# passive players
inactive_plays = []

# drawn cards
drawncards = []

#assigned turns
asst_turns = []
turns = [1,2,3,4,5]

### ### Static Values ### ###

#initialize joined_players (the number of players that are in/have joined the game) to 0
joined_players = 0

#game needs to init to 'not over'
gameisover = 0

AllCords ={
    '0':(0,0),
    '1':(2,0),
    '2':(4,0),
    '3':(0,2),
    '4':(2,2),
    '5':(4,2),
    '6':(0,4),
    '7':(2,4),
    '8':(4,4),
    'a':(1,0),
    'b':(3,0),
    'c':(0,1),
    'd':(2,1),
    'e':(4,1),
    'f':(1,2),
    'g':(3,2),
    'h':(0,3),
    'i':(2,3),
    'j':(4,3),
    'k':(1,4),
    'l':(3,4),
    'm':(-1,1),
    'n':(-1,3),
    'o':(3,-1),
    'p':(5,1),
    'q':(1,5),
    'r':(3,5)
}

roomspaces = ['0','1','2','3','4','5','6','7','8']
#####################################################


############################################################################################
# FUNCTIONS
############################################################################################

#lookup_space()
# function for looking up a space given a position (grid coordinates)
# iterates through the dictionary of valid positions 
def lookup_space(search_position):
    for space, position in AllCords.iteritems():
        if position == search_position:
            return space

def storepos(self):
    self.position = (self.xcord,self.ycord)
    self.space = lookup_space(self.position)
    opos[self.id]=self.position
    
def storexypos(self):
    self.xcord = self.position[0]
    self.ycord = self.position[1]
    self.space = lookup_space(self.position)
    opos[self.id]=self.position

def valid_space(posit):
    # check to see if the space is occupied
    for s, p in opos.iteritems():
        if p == posit:
            return False
    # check to see if the space is a valid space (to make sure that people can't move off of the board)
    for s, p in AllCords.iteritems():
        if p:
            return True
        return False

def dealcard():
    random_card = choice(ddeck.keys())
    while random_card in drawncards:
        random_card = choice(ddeck.keys())
    if random_card not in drawncards:
        drawncards.append(random_card)
        return random_card
        
def gen_rturn():
#    print "generating random turn"
    random_order = choice(turns)
#    print "random order:", random_order
    while random_order in asst_turns:
#        print "condition 1"
        random_order = choice(turns)
        if len(asst_turns) == 5:
            break
    if random_order not in asst_turns:
#        print "condition 2"
        asst_turns.append(random_order)
        return random_order
    

def dealwep():
    random_card = choice(wdeck.keys())
    while random_card in drawncards:
        random_card = choice(wdeck.keys())
    if random_card not in drawncards:
        drawncards.append(random_card)
        return random_card

def dealroom():
    random_card = choice(rdeck.keys())
    while random_card in drawncards:
        random_card = choice(rdeck.keys())
    if random_card not in drawncards:
        drawncards.append(random_card)
        return random_card

def dealplay():
    random_card = choice(pdeck.keys())
    while random_card in drawncards:
        random_card = choice(pdeck.keys())
    if random_card not in drawncards:
        drawncards.append(random_card)
        return random_card

def GameStart():
    for i in players.values():
        if i.name == 'miss scarlet':
            i.turnorder = 0
        else:
            i.turnorder = gen_rturn()

class UniqueID(object):
    def __init__(self, initial_value=0):
        self.id = initial_value
    
    def next_id(self):
        tmp = self.id
        self.id += 1
        return tmp

class Player(object):

    id_gen = UniqueID()

    def __init__(self, name, Position):
        self.position = Position
        self.xcord = self.position[0]
        self.ycord = self.position[1]
        self.space = lookup_space(self.position)
        self.id = Player.id_gen.next_id()
        self.name = name

    def move(self, m_dir):
        if m_dir == 'diag':
            print "diag"
            if self.space == '0':
                self.position = AllCords['8']
                self.space = lookup_space(self.position)
                storepos(self)
            elif self.space == '6':
                self.position = AllCords['2']
                self.space = lookup_space(self.position)
                storepos(self)
            elif self.space == '8':
                print "set position to pos 8"
                self.position = AllCords['0']
                print "set space to space", lookup_space(self.position)
                self.space = lookup_space(self.position)
                storepos(self)
            elif self.space == '2':
                self.position = AllCords['6']
                self.space = lookup_space(self.position)
                storepos(self)
        elif m_dir == 'up':
            print "up"
            self.ycord = self.ycord-1
            storepos(self)
            print "player has moved up to position:", self.position
            print "player has moved to space:", self.space
        elif m_dir == 'down':
            print "down"
            self.ycord = self.ycord+1
            storepos(self)
        elif m_dir == 'left':
            print "left"
            self.xcord = self.xcord-1
            storepos(self)
        elif m_dir == 'right':
            print "right"
            self.xcord = self.xcord+1
            storepos(self)
    def moveto(self, target):
        print "moveto target:", target
        self.position  = target
#        storepos(self)
        storexypos(self)
        print "current square", self.space
        print "current position", self.position

# Suggestions
def suggest(suggesting_player):
    foo0 = Message(suggesting_player, "it is your turn to make a suggestion\n")
    foo0.send()
    card1 = raw_input("card1 (person):\n")
    card2 = raw_input("card2: (room) \n")
    card3 = raw_input("card3: (weapon) \n")
    foo = Message(all, "%s has made a suggestion:\n" % (suggesting_player))
    foo1 = Message(all, "%s,%s, %s\n" % (card1,card2,card3))
    # move player to the room that the accusation is happening in...
    print "player's position prior to move", players[card1].position
    print "suggesting player position:", suggesting_player.position
    players[card1].moveto(suggesting_player.position)
    print "player position after:",players[card1].position
    foo.send()
    foo1.send()
    sug_response()

def sug_response():
    # iterate through all of the inactive players
    for i in players:
        if i != active_player:
            inactive_plays.append(i)
    for i in inactive_plays:
        msg1 = Message(i, "you have cards %s" % players[i].cards)
        msg1.send()
        resp_choice = raw_input("Please enter which card you wish to respond with\n")
        msg2 = Message(active_player, resp_choice)
        msg2.send()

def taketurn(aplayer):
    global active_player
    active_player = aplayer
    foo = Message(aplayer, "it is your turn, please select a move:\n")
    foo.send()
    ans_choice1 = raw_input("up, down, left, right\n")
    aplayer.move(ans_choice1)
    # if the player has entered a room, prompt for a suggestion:
    if aplayer.space in roomspaces:
        suggest(aplayer)
    foo1 = Message(aplayer, "would you like to make an accusation?")
    foo1.send()
    ans_choice2 = raw_input("yes or no?\n")
    if ans_choice2 == "yes":
        accuse(aplayer)
    else:
        nextturn(aplayer)
    
def nextturn(aplayer):
    #debug - print current turn
    print aplayer.turnorder
    if aplayer.turnorder != 5:
        nextplayerturn = aplayer.turnorder + 1
    else:
        nextplayerturn = 0
    print "next player turn #"
    for i in players.values():
        if i.turnorder == nextplayerturn:
            print "next player is:", i.name
            if gameisover == 0:
                #debug
                print "condition met"
                nextplayer = i
                break
    taketurn(nextplayer)
                
    
def accuse(s_player):
    global gameisover
    global answer
    foo = Message (s_player, "please enter which three cards you believe are the answer:\n")
    foo.send()
    #debug
    print "the answer is : ", answer
    ans_choice1 = raw_input("card1 (person):\n")
    ans_choice2 = raw_input("card2: (room) \n")
    ans_choice3 = raw_input("card3: (weapon) \n")
    foo1 = Message(all, "%s has made an accusation:\n" % (s_player))
    foo1.send()
    if answer == [ans_choice1, ans_choice2, ans_choice3]:
        foo2 = Message(s_player, "Your answer is correct, you have won the game")
        foo2.send()
        foo3 = Message(all, "Player %s has won the game, the game is now over" % (s_player))
        foo3.send()
        gameisover = 1
    else:
        foo4 = Message(all, "The accusation was incorrect, player %s has now been kicked from the game" % (s_player))
        foo4.send()

# CountPlayers is a funciton that acts as a 3 minute timer checking to see if enough players have joined the game
# the function checks the number of players once per second and decrements the 'cnt' variable
def CountPlayers(n_players):
#debug
#- seeing that this is fired and counting down:
    print "counting how many players have joined:", n_players
    cnt = 3 * 60
    while cnt > 0:
        print "count started"
        if joined_players == 6:
            return
        cnt -= 1
        #debug - testing the counting/sleep function
        print "count:", cnt
        sleep(1)

## Card Class
class Card(object):

    id_gen = UniqueID()
    cids = []  # Keep track of all cards created thusfar
    # Deal a random card from the set of all created cards
    # q1: Why are we doing this?
    # q2: Why are we identifying cards by a numeric ID, when we could be just storing a
    #     reference to their unique instance?
    @staticmethod
    def deal_random_card():
        return choice(Card.cids)
    
    def __init__(self, Title, Type):
        self.type = Type
        self.title = Title
        self.id = Card.id_gen.next_id()
        self.cids.append(self.id)

    def __repr__(self):
        return 'The %s card' % self.type


class Message(object):
    Name = "Message"
    def __init__(self, Target, Msgtext):
        self.target = Target
        self.msgtxt = Msgtext
    def send(self):
        #target = self.target
        #msgtext = self.msgtext
        """
            this will only print for now to all users (since there's only 1 notional user in this mock-up but it should be able to be modified in the future to work with multiple clients)
            """
        
        if self.target != "all":
            print "this message is for playerid:", self.target
            print "message contents:", self.msgtxt
        else:
            print "this message is for all players:"
            print "message contents:", self.msgtxt

#####################################################


############################################################################################
# INSTANTIATE GAME STATE
############################################################################################

### ### Instantiate all of the Cards ### ###
def createCards(weps, rooms, plays):
    for i in weps:
        ddeck[i] = Card(i, 'weapon')
        wdeck[i] = Card(i, 'weapon')
    for i in rooms:
        ddeck[i] = Card(i, 'room')
        rdeck[i] = Card(i, 'room')
    for i in plays:
        ddeck[i] = Card(i, 'player')
        pdeck[i] = Card(i, 'player')

### ### Instantiate players ### ###
def createPlayers(plays):
    for i in plays:
        players[i] = Player(i,(0,0))

def startingPositions(players):
    for i in players.values():
        if i.name == 'mr. green':
            i.position = (1,5)
            storexypos(i)
            # q
        if i.name == 'miss scarlet':
            i.position = (3,-1)
            storexypos(i)
            # o
        if i.name == 'mrs. white':
            i.position = (3,5)
            storexypos(i)
            # r
        if i.name == 'mrs. peacock':
            i.position = (-1,3)
            storexypos(i)
            # n
        if i.name == 'colonel mustard':
            i.position = (5,1)
            storexypos(i)
            # p
        if i.name == 'professor plum':
            i.position = (-1,1)
            storexypos(i)
            # m

############################################################################################
# Begin Game (turn implementation)
############################################################################################
def joingame(s_player, n_players):
    msg1 = Message(s_player, "Would you like to join the game?")
    msg1.send()
    bool_join = raw_input("would you like to join the game? (yes or no)\n")
    if bool_join == 'yes':
        msg2 = Message(s_player, "you may play as any of the following characters:\n")
        msg2.send()
        player_character = raw_input("choose a character (type in their name)")
        # increment the number of players that the game knows have joined
        n_players = n_players + 1
        # announce globally that someone has joined the game
        msg3 = Message(all, "player has chosen character %s" % (player_character))
        msg3.send()
        # tell the player what cards they have:
        msg4 = Message(s_player, "Your cards are %s" % (players[player_character].cards))
        msg4.send()


def main():
# ---> I think this is where the players should all be instantiated in their different starting spaces (although does it matter?)

# ----> this is probably also the point where a turn order should be set/agreed on?
    global answer

    ### ### Track the number of players that have joined the game ### ###
    joined_players = 0

    ### ### Track who the active player is (whose turn it is) ### ### 
# init active_player to miss scarlet since she always goes first
    active_player = 'miss scarlet'
    createCards(weapon_names,rm_names, player_names)
    createPlayers(player_names)
    answer = [dealplay(), dealroom(), dealwep()]
    startingPositions(players)
    
#####################################################

#debug
    print "the answer is : ", answer

    for i in players.values():
        i.cards=[dealcard(), dealcard(), dealcard()]
    GameStart()
    taketurn(players['miss scarlet'])

#    taketurn(players['miss scarlet'])

#debug
#- turnorder
#    for i in players.values():
#        print "player name:", i.name
#        print "player turnorder:", i.turnorder

#    for i in players.values():
#        print "player name:", i.name
#        print "player cards:", i.cards

#    print "mr. green's position", players['mr. green'].position
#debug
# tests the player movement in the game outside of the turn construct
#move mr. green down one space
#    players['mr. green'].move('up')
#    print "mr. greens's new y coordinate:", players['mr. green'].ycord
#    print "mr. green's position", players['mr. green'].position
#    print "mr. green's square occupied", players['mr. green'].space

#debug
# - tests the suggest() function outside of the turn construct
#    suggest('mr. green', 'knife', 'kitchen', 'mrs. peacock')

#debug - funcitonality testing outside of turn state:
#sug_response()

#debug
# tests out the accuse() function in the code
#    accuse('mr. green')
    

if __name__ == '__main__':
    main()

    
